<?

/** Source entity for HTML (and XML or any other input source) form.
 *
 *  This collection of functions (libary) is licensed under GPL2.
 *  See LICENSE or www.gnu.org for more details.
 *
 *  @author Jyry Kuukkanen
 *
 *  $Id: dzsrcentity.php 597 2006-04-03 16:15:46Z jyry $
 */


include_once("dzconst.php");
include_once("soset.php");


/** Html/Xml/whatever entity class to be used with datazet.
 *  @var soSet $__Properties Entity properties: opening tag in HTML and other
 *  @var SOET_* $Type HTML, TABLE, TD, etc - (see SOET_*)
 *  @var string $Name [Table.]Column name
 *  @var int $Level Entitylevel (see SOET_*)
 *  @var string $Value Value of html entity, or tag
 *  @var string $PreCodeCode before entity and up to tagname "<TAG "
 *  @var string $PostCode End tag "</TAG>" code
 *  @var string $PreValue Optional code inside value, but before dz definition
 *  @var string $PostValue Optional code inside value, but after dz definition
 *  @var array/int $Children List of child elements
 *  @var int $ParentNo No. of parent element
 */
class dzSourceEntity {

    /** @access private */
    var $__Properties;

    /** @access public */
    var $Type;
    var $Name;
    var $Level;
    var $Value;
    var $PreCode;
    var $PostCode;
    var $PreValue;
    var $PostValue;
    var $Children;
    var $ParentNo;


    /** constructor
     *  Initialises properties.
     */
    function dzSourceEntity() {
        $this->__Properties = new soSet();
        $this->Children = array();
    } // constructor


    /** Stores a property value.
     *  Properties are in the opening tag of the entity, e.g. <TD prop=value>
     *  @param string $KeyProperty key id.
     *  @param any $ValueValue to store
     *  @param bool $Replace When set, replaces any exsiting property
     *  @return bool Returns true(1) if Replace is set or Key does
     *      not exist in the set, otherwise returns false(0).
     */
    function setProperty($Key, $Value, $Replace = 1) {
        $Key = strtolower($Key);
        return $this->__Properties->setItem($Key, $Value, $Replace);
    } // setProperty


    /** Gets a property value.
     *  @param string $KeyProperty key id.
     *  @return any Returns property value or NULL if key does not exist.
     */
    function getProperty($Key) {
        $Key = strtolower($Key);
        if (!$this->__Properties->itemExists($Key)) return NULL; // <-- EXIT
        return $this->__Properties->getItem($Key);
    } // getProperty


    /** Store properties from a string to soSet
     *  @param string $PropertyStr Space delimited list of key=value pairs.
     *  @param bool $Replace When set, replaces any exsiting properties
     *  @return int Returns number of properties stored. If Replace is not set,
     *    then all existing peroperties are left untouched.
     */
    function setProperties($PropertyStr, $Replace = 1) {

        /* I'm not very proud of this code, so anyone interested converting it
             to some stunning regexp code, please help yourself
             --jq
           */

        /* Properties in the opening tag any of the following form:
             name --no value
             name=value --name and value
             name="value1 more2" --name and value in quotes

             N.B. there may be a escaped quote inside quotes '\"'
             N.B. equal sign may have space before and/or after: ' = '
         */


        /* To begin with, clean up the input string:
             1) convert tabs, newlines and crs to spaces
             2) convert double spaces to single
             3) remove spaces around "=" sign
           */
        $rest = soSwapAll(trim($PropertyStr),
                          array("\t", "\n", "\c", "  ", " =", "= "),
                          array(" ", " ", " ", " ", "=", "="));

        if (!$rest) return 0; // <-- EXIT

        /* Loop through all name=value pairs.
             A prop name without value is also acceptable.
           */
        $rest = array("", $rest);
        while ($rest[1] != "") {
            /* get property name */
            $name = "";
            $pcount = strlen($rest[1]);

            /* find out 1st '=' or ' ' char position - whichever comes 1st */
            $p1 = soStrPos($rest[1], "=");
            $p2 = soStrPos($rest[1], " ");
            if (($p1 != -1) && ($p2 != -1)) {
                $p = min($p1, $p2);
            } else {
                $p = max($p1, $p2);
            };


            $name = soSubStr($rest[1], 0, $p);

            /* get value, if any */
            $value = "";
            if (($p != -1) && ($rest[1][$p] == "=")) {
                $rest = soSplitStr($rest[1], "=", 1, 0, 0);
                if ($rest[1][0] == "\"") {
                    $p = 0;
                    $value = "\"";
                    do {
                        $p++;
                        $value .= $rest[1][$p];
                    } while (!(($rest[1][$p] == "\"") &&
                             ($rest[1][$p -1] != "\\")));
                        $rest[1] = substr($rest[1], $p +1,
                                          strlen($rest[1]) -$p -1);

                } else { // ($rest[1][0] == "\"")
                    if (!strstr($rest[1], " ")) $rest[1] .= " ";
                    $rest = soSplitStr($rest[1], " ", 1, 0, 0);
                    $value = $rest[0];
                }; // else if
            } else {; // $rest[1][$p] == "="
                $len = strlen($rest[1]);
                if ($p == -1) {
                    $rest = array("", "");
                } else {
                    $rest = array("", trim(soSubStr($rest[1], $p, $len -$p)));
                };
            };

            /* right, we now ought to have $name and possible $value */
            if ($name != "") $this->setProperty($name,
                                                strtolower($value));
        }; // while $rest

    } // setProperties


    /** Gets properties as a string.
     *  @return str Returns all the properties as a space delimited string of
     *  key=value pairs (or "key" only without a "=value" bit)
     *    then all existing peroperties are left untouched.
     */
    function getProperties() {
        $result = "";
        $keys = $this->__Properties->getKeys();
        foreach ($keys as $key) {
            $result = soJoinStr($result, " ", $key, 1);
            $prop = $this->__Properties->getItem($key);
            if (!soIsEmptyStr($prop)) $result .= "=".$prop;
        }; // for

        return $result;
    } // setProperties

}; // dzSourceEntity


/** Filter structure used by Groups and Purkki.
 *  This contains properties to store information of source
 *  column and current focus row to apply filter by.
 *  @var arrar/string $Seed Source column name
 *  @var array/string $Pot Columns to compare source vals to
 *  @var int $FocusRow Current row no or -1 (none)
 *  @var int $FocusCol Current column name or "" (none)
 *  @var array/any $Value Empty array == no filter value set. Element count
 *       must match with group table(s) major key count (all key columns except
 *       the last index column)
 */
class dzFilsu {
    var $Seed;
    var $Pot;
    var $FocusRow = -1;
    var $FocusCol = "";
    var $Value;
}; // dzPurkkiFilter


/** Structure to hold entity group properties.
 *  This class is used by dzLaituri, dzAsm and dzTemplate. It holds properties
 *  that are common to several columns that are to be modified together,
 *  like inserting or removing a row.
 *  @var DZGT_* $Type Group type: main, sub
 *  @var array/string $Cols Struct column names
 *  @var int $ParentNo Parent src entity no. if any
 *  @var array/bool $Required Array of col names that are required
 *  @var string $AutoNum Column name that should be renumbered automatically
 *  @var DZGI_* $Inserting Inserting rows restriction
 *  @var int $InsCount Rows inserted since start of edit
 *  @var DZGD_* $Deleting Deleting rowsrestriction
 *  @var int $DelCount Rows deleted since start of edit
 *  @var DZGD_* $RowLimit Max number of rows (0==no limit)
 *  @var int $NewRowAt New row position, -1==no new rows in group
 *  @var dzFilsu $Filter Filter properties
 */
class dzGroup {
    var $Type;
    var $Cols;
    var $ParentNo;
    var $Required;
    var $AutoNum;
    var $Inserting;
    var $InsCount = 0;
    var $Deleting;
    var $DelCount = 0;
    var $RowLimit = 0;
    var $NewRowAt = -1;
    var $Filter;

    /** Consturcor
     */
    function dzGroup() {
        $this->Cols = array();
        $this->Required = array();
        $this->Filter = new dzFilsu();
    } // dzGroup

}; // dzGroup


/** Sub structure for dzSECache
 *  This holds source entities for each page of the form.
 *  @var array/dzSource $Entities Entity All source entities
 *  @var soSet/string $Controls Data column control features ini
 *  @var soSet/string $Formats Data column format feature ini
 *  @var array/int $CellNos Indexnos of cells in Entities
 *  @var array/int $DataCells Entity nos. that contains dataxi colums value
 *  @var array/int $TextCells Entity nos. that contains just text to be
 *       be possibly translated
 *  @var time $SourceName Source file name (if file used as source)
 *  @var time $SourceTime Source file mtime (if file used as source)
 *  @var string $Title Title of the page
 *  @var array/string $UserGroups Groups names that has access to this page
 *  @var int $UserLevel User level that has access to this page
 *  @var array/int $InnterTables Entity nos. that contain tables that holds
 *      data cells - hence inner tables as opposite to outline tables.
 *  @var string $Align Alignment of the page. This is fetched from the 1st
 *      table encountered on the page and is used by dzAsm/getPageHtml.
 */
class dzSEPageCache {
    var $Entities;
    var $Controls;
    var $Formats;
    var $CellNos;
    var $DataCells;
    var $TextCells;
    var $SourceTime;
    var $SourceName;
    var $Title;
    var $UserGroups;
    var $UserLevel;
    var $InnerTables;
    var $Align = -1;

    /** Constructor
     *  Initialises properties.
     */
    function dzSEPageCache() {
        $this->Entities = array();
        $this->Controls = new soSet();
        $this->Formats = new soSet();
        $this->CellNos = array();
        $this->DataCellNos = array();
        $this->SourceTime = NULL;
        $this->UserGroups = array();
        $this->InnerTables = array();
    } // dzSEPageCache


}; // dzSEPageCache


/** Structure to hold entities and lots of other stuff.
 *  This class is used by dzTemplate and dzAsm. It holds the all the source
 *  entities and other properties that the dzParser returns.
 *  @var array/dzSEPageCache $Pages Form pages source entity cache
 *  @var string $DbAlias Database alias name for kone
 *  @var string $DBTT Database translation table name
 *  @var string $StructIni Ini data for dzstringuct
 *  @var string $StructName Form/structure name
 *  @var string $InterfacesIni Ini data for dzInterfaces
 *  @var string $FormOptionsIni Ini data for dzFormOptions
 *  @var array/int $GroupNos Shortcut list of entity pos' that contains a row
 *       element that is to be repeated as many times as there are rows in
 *       dzPurkki object for related col(s).
 *  @var soSet/dzGroup $Groups Group definitions to restrict deleting and
 *       inserting of rows and handling new row positions
 */
class dzSECache {
    var $Pages;
    var $DbAlias;
    var $DBTT;
    var $StructIni;
    var $StructName;
    var $InterfacesIni;
    var $FormOptionsIni; // ---> PageOptionsIni
    var $GroupNos;
    var $Groups;

    /** Constructor
     *  Initialises properties.
     */
    function dzSeCache() {
        $this->Pages = array();
        $this->StructIni = "";
        $this->FormOptionsIni = "";
        $this->GroupNos = array();
        $this->Groups = new soSet();
    } // dzSeCache


    /** Adds a new page to the cache
     */
    function addPage() {
        $this->Pages[] = new dzSEPageCache();
    } // addPage


    /** Checks whether all page source files are up to date.
     *  Loops through all pages and checks their date stamp to the one given
     *  and returns true if all pages are up to date.
     *  @param int $TimeStamp Time stamp to compage page stamps to
     *  @return bool True when all page files are up to date, otherwise false.
     */
    function checkPageStamps($TimeStamp) {
        $path = dzGetiniPath(DZID_FORM_SOURCE_FOLDER);
        $result = 1;

        /* Loop all pages but first, as it is checked already in dztemplate */
        $p = count($this->Pages);
        while ((--$p) && ($result)) {
            $page = &$this->Pages[$p];
            $src_time = $path."/".@filemtime($page->SourceName);
            if ($TimeStamp != $src_time) $result = 0;
        }; // for

        return $result;
    } // checkPageStamps


    /** Returns StructIni or FormOptionsIni property
     *  @param string||int $Type Either "struct", "form" or "interface"
     *  @param bool $ClearWhen set, clears the property, too.
     *  @return string Ini Returns ini string, or NULL when type is illegal
     */
    function extractIni($Type, $Clear = 0) {
        switch ($Type) {
            case "struct":
                $result = $this->StructIni;
                if ($Clear) $this->StructIni = "";
                break;
            case "form":
                $result = $this->FormOptionsIni;
                if ($Clear) $this->FormOptionsIni = "";
                break;
            case DZCP_IFACE:
            case "interface":
            case "interfaces":
                $result = $this->InterfacesIni;
                if ($Clear) $this->InterfacesIni = "";
        }; // switch

        return $result;
    } // extractFormIni


}; // dzSECache


/** Returns groupd is to store groups for each page.
 *  Due to late implementation of sub-pages in Dataxi, a quick hack was
 *  introduced where groupd id is formed of entity no. (group no.) and
 *  page no.
 *  @param int $PageNo Page no.
 *  @param int $GroupNo Group no. (entity no).
 *  @return int Group's ID
 */
function dzGetGroupID($PageNo, $GroupNo) {
    return ($GroupNo *100) +$PageNo;
}; // dzGetGroupID


/** Splits GroupID into page and group no.
 *  @param int $GroupID Groups ID, formed using dzGetGroupID
 *  @return array/int Array of two elements: ["page"] and ["group"]
 */
function dzSplitGroupID($GroupID) {
    $page_no = ($GroupID % 100);
    $group_no = $GroupID -$page_no;
    return array("page" => $page_no, "group" => $group_no);
}; // dzGetGroupID


?>
